Implement player profile creation flow and profile page (#211, #213)#251
Conversation
Adds the full player profile feature: first-visit onboarding gate, persistent UserProfile aggregate in CosmosDB, silent migration of legacy alias-only users, and /profile page with alias editing for both React and Blazor frontends. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Implements a first-class “user profile” concept (create + view + edit alias) across the backend and both React/Blazor frontends, replacing the legacy auto-generated alias flow with an onboarding gate and migration path.
Changes:
- Backend: add
UserProfiledomain aggregate + profile commands/queries +ProfilesControllerendpoints backed by a new Cosmos DBprofilescontainer. - React + Blazor: add
/create-profileand/profilepages and implement localStorage-based profile initialization/migration flows. - Tests/infra: add unit tests for profile domain/handlers and provision the new Cosmos container via Bicep.
Reviewed changes
Copilot reviewed 49 out of 49 changed files in this pull request and generated 10 comments.
Show a summary per file
| File | Description |
|---|---|
| src/frontend/Sudoku.React/src/types/index.ts | Adds profile-related TypeScript types. |
| src/frontend/Sudoku.React/src/pages/ProfilePage.tsx | Implements profile display + alias edit UI. |
| src/frontend/Sudoku.React/src/pages/ProfilePage.module.css | Styles for React profile page. |
| src/frontend/Sudoku.React/src/pages/CreateProfilePage.tsx | Implements profile creation UI + localStorage write. |
| src/frontend/Sudoku.React/src/pages/CreateProfilePage.module.css | Styles for React create-profile page. |
| src/frontend/Sudoku.React/src/hooks/usePlayerService.ts | Implements profile gate + legacy alias migration logic. |
| src/frontend/Sudoku.React/src/api/apiClient.ts | Adds profile API calls with status-returning helper. |
| src/frontend/Sudoku.React/src/App.tsx | Registers /create-profile and /profile routes. |
| src/frontend/Sudoku.Blazor/Services/PlayerManager.cs | Adds profile initialization/migration flow for Blazor. |
| src/frontend/Sudoku.Blazor/Services/LocalStorageService.cs | Adds localStorage profile read/write helpers + alias removal. |
| src/frontend/Sudoku.Blazor/Services/JSRuntimeWrapper.cs | Adds localStorage remove wrapper. |
| src/frontend/Sudoku.Blazor/Services/HttpClients/PlayerApiClient.cs | Adds profile create/get/update HTTP calls. |
| src/frontend/Sudoku.Blazor/Services/HttpClients/IPlayerApiClient.cs | Extends client interface with profile operations. |
| src/frontend/Sudoku.Blazor/Services/Abstractions/IPlayerManager.cs | Extends manager interface for profile flows. |
| src/frontend/Sudoku.Blazor/Services/Abstractions/ILocalStorageService.cs | Extends local storage abstraction for profiles. |
| src/frontend/Sudoku.Blazor/Services/Abstractions/IJSRuntimeWrapper.cs | Extends JS runtime abstraction with remove. |
| src/frontend/Sudoku.Blazor/Models/ProfileInfo.cs | Adds local profile info model (id + alias). |
| src/frontend/Sudoku.Blazor/Models/ProfileDto.cs | Adds profile DTO model for API responses. |
| src/frontend/Sudoku.Blazor/Models/ApiResult.cs | Adds status code tracking to API result model. |
| src/frontend/Sudoku.Blazor/Components/Pages/Profile.razor.cs | Implements profile load + alias update logic (code-behind). |
| src/frontend/Sudoku.Blazor/Components/Pages/Profile.razor | Adds Blazor profile page UI. |
| src/frontend/Sudoku.Blazor/Components/Pages/Index.razor.cs | Gates index initialization on profile initialization. |
| src/frontend/Sudoku.Blazor/Components/Pages/CreateProfile.razor.cs | Implements profile creation logic (code-behind). |
| src/frontend/Sudoku.Blazor/Components/Pages/CreateProfile.razor | Adds Blazor create-profile page UI. |
| src/backend/Tests/Mocks/MockPlayerManagerExtensions.cs | Updates mocks to satisfy new profile init calls. |
| src/backend/Tests/Mocks/MockLocalStorageServiceV2Extensions.cs | Updates mocks for new GetProfileAsync behavior. |
| src/backend/Tests/Domain/UserProfileTests.cs | Adds unit tests for UserProfile aggregate behavior/events. |
| src/backend/Tests/Application/Handlers/UpdateProfileAliasCommandHandlerTests.cs | Adds tests for update-alias handler logic. |
| src/backend/Tests/Application/Handlers/CreateProfileCommandHandlerTests.cs | Adds tests for create-profile handler logic. |
| src/backend/Sudoku.Infrastructure/Repositories/CosmosDbUserProfileRepository.cs | Adds CosmosDB-backed repository for profiles. |
| src/backend/Sudoku.Infrastructure/Models/UserProfileDocument.cs | Defines Cosmos document shape for profiles. |
| src/backend/Sudoku.Infrastructure/Mappers/UserProfileMapper.cs | Maps between domain UserProfile and Cosmos document. |
| src/backend/Sudoku.Infrastructure/Configuration/InfrastructureServiceCollectionExtensions.cs | Registers IUserProfileRepository implementation. |
| src/backend/Sudoku.Domain/ValueObjects/ProfileId.cs | Introduces ProfileId value object. |
| src/backend/Sudoku.Domain/Events/ProfileEvents.cs | Adds profile domain events. |
| src/backend/Sudoku.Domain/Entities/UserProfile.cs | Adds the UserProfile aggregate root. |
| src/backend/Sudoku.Application/Queries/GetProfileByAliasQuery.cs | Adds profile read query contract. |
| src/backend/Sudoku.Application/Interfaces/IUserProfileRepository.cs | Adds repository abstraction for profiles. |
| src/backend/Sudoku.Application/Handlers/UpdateProfileAliasCommandHandler.cs | Implements alias update command handler. |
| src/backend/Sudoku.Application/Handlers/GetProfileByAliasQueryHandler.cs | Implements get-profile-by-alias query handler. |
| src/backend/Sudoku.Application/Handlers/CreateProfileCommandHandler.cs | Implements create-profile command handler. |
| src/backend/Sudoku.Application/DTOs/ProfileDto.cs | Adds application-layer ProfileDto. |
| src/backend/Sudoku.Application/Common/ICommandOfT.cs | Adds generic command/handler interfaces. |
| src/backend/Sudoku.Application/Commands/UpdateProfileAliasCommand.cs | Adds update-alias command contract. |
| src/backend/Sudoku.Application/Commands/CreateProfileCommand.cs | Adds create-profile command contract. |
| src/backend/Sudoku.Api/Models/UpdateProfileAliasRequest.cs | Adds PATCH request model. |
| src/backend/Sudoku.Api/Models/CreateProfileRequest.cs | Adds POST request model. |
| src/backend/Sudoku.Api/Controllers/ProfilesController.cs | Adds Profiles API endpoints. |
| infra/modules/storage.bicep | Provisions CosmosDB profiles container + throughput. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
…key, and transient error handling - Rewrite usePlayerService.test.ts to match profile-based initialization flow (fixes CI) - Fix re-initialization loop after navigate by guarding with navigatingRef - Change CosmosDB profiles container partition key from /alias to /profileId (stable partition; alias changes no longer require delete+create or leave orphaned documents under old alias partition) - Add ProfileErrorCodes constants and ErrorCode property to Result<T>/Result to replace brittle substring matching in ProfilesController - Add same-alias no-op in UpdateProfileAliasCommandHandler (saves unchanged alias was rejected as taken) - Surface transient backend errors (non-404) as error state rather than redirecting to /create-profile in both React hook and Blazor PlayerManager - Use Random.Shared instead of new Random() in Blazor PlayerManager suffix retry - Update CreateProfilePage copy to reflect alias is editable from profile page Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
Addressed all Copilot review comments in commit e774f66: CI failure fixed
Structured error codes (replaces brittle substring matching)
CosmosDB partition key changed from
Same-alias no-op in Transient error handling in React hook and Blazor PlayerManager — non-404 backend errors (500/503) now surface as error state / throw instead of redirecting to
UI copy updated — "cannot be changed easily later" replaced with "You can update it later from your profile page" Note on game migration: the |
The UsePlayerServiceReturn interface now includes profileId as a required field; existing page test mocks were missing it, causing TypeScript errors in CI. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
Azure Static Web Apps: Your stage site is ready! Visit it here: https://gray-bush-02024e71e-251.westus2.7.azurestaticapps.net |
Summary
UserProfileaggregate,ProfileIdvalue object, andProfileCreatedEvent/ProfileAliasUpdatedEventdomain events toSudoku.DomainCreateProfileCommand,UpdateProfileAliasCommand, andGetProfileByAliasQueryhandlers with lowercase normalization and alias uniqueness checksCosmosDbUserProfileRepositorybacked by a newprofilesCosmosDB container (provisioned in Bicep) with partition key/aliasand unique key policyProfilesControllerwithPOST /api/profiles,GET /api/profiles/{alias}, andPATCH /api/profiles/{alias}endpointsusePlayerServicewith full gate + silent migration flow (FR-1, FR-5, FR-9); adds/create-profileand/profilepagesPlayerManagerwithEnsureProfileInitializedAsyncmigration flow; addsCreateProfile.razorandProfile.razorpagesUserProfile,CreateProfileCommandHandler, andUpdateProfileAliasCommandHandler; all 728 tests passTest plan
/create-profileand can choose an aliassudoku-aliasin localStorage is silently migrated tosudoku-profileon next loadsudoku-profilebypasses the creation flow/profilepage shows alias and member-since date; inline edit saves new alias and updates localStoragedotnet testpasses all 728 testsCloses #211, #213
🤖 Generated with Claude Code